# coding=utf-8
import inspect
import json

from SumPredict import *
from WorkManager import *
from database.models.Normal import Normal
from database.models.OperationSet import OperationSet
from database.models.TimingSwitch import TimingSwitch


def SetTaskInRunning(task, isRun):
    s = Session()
    tms = s.query(TimingSwitch).with_for_update(read=False, nowait=False).filter_by(id=task.id).all()
    if len(tms) == 1:
        tms[0].IsRunning = 1 if isRun else 0
    try:
        s.commit()
    except Exception as e:
        logging.warn("commit error", e.message)


class TimeWorker(threading.Thread):
    def __init__(self, task):
        self.task = task
        super(TimeWorker, self).__init__()
        self.WorkingThread_args = None

    def __del__(self):
        pass

    def TimeWorkerThread(self, d):
        stepID = d

        # engine1 = create_engine('sqlite:///ai-ele.db', connect_args={'check_same_thread': False},
        #                        poolclass=SingletonThreadPool, echo=False)
        # engine1.execute('pragma foreign_keys=on')
        # Session1 = sessionmaker(bind=engine1)
        # s = Session1()
        s = Session()
        nid = int(stepID)
        opts = s.query(OperationSet).filter_by(NormalID=nid).all()
        if len(opts) < 1:
            Session.remove()
            return json.encoder({'Code': 1, 'desc': u'未找到对应动作'})
        else:
            for i in range(0, len(opts)):
                time.sleep(1.0 / 5)
                if not self.is_alive():
                    Session.remove()
                    logging.warn("work is not active")
                    return
                if opts[i].OptTypeID is None:
                    logging.warn("opt type is not available")
                    continue
                else:
                    opt = opts[i].OptTypeID
                if opts[i].PointID is None:
                    logging.warn('point is not available')
                    continue
                else:
                    point_id = opts[i].PointID
                nowstat = s.query(ControllerPoint, Controller).with_for_update(read=False, nowait=False).filter(
                    ControllerPoint.ControllerID == Controller.id).filter(ControllerPoint.id == point_id).all()
                if len(nowstat) != 1:
                    Session.remove()
                    return json.dumps({"Code": 1, 'desc': u'内部执行条件不满足'})
                cur_value = nowstat[0].Controller.Value
                pointIndex = nowstat[0].ControllerPoint.PointIndex
                hard_id = int(nowstat[0].Controller.HardID)
                controllerID = int(nowstat[0].Controller.id)
                if opt == 4:
                    t = threading.Thread(target=self.WorkingThread, args=(s['Params'],))
                    t.start()
                    t.join()
                if opt == 3:
                    # 延时
                    dly = opts[i].Params
                    logging.debug('delay:%s s' % dly)
                    time.sleep(1.0 * int(dly) / 1000)
                if opt is 2:
                    # 关闭一个灯
                    cl = int(cur_value) & 0xff
                    ch = int(cur_value) & 0xff00
                    ch = ch >> 8
                    # 通过point index 组装 发送数据
                    l = 0
                    h = 0
                    oneN = 1
                    if int(pointIndex) > 8:
                        h = oneN << (int(pointIndex) - 8 - 1)
                    else:
                        l = oneN << int(pointIndex - 1)
                    h = ~h & ch
                    l = ~l & cl
                    # 写会数据库下次使用
                    currentValue = (h << 8 | l) & 0xffff
                    controller = s.query(Controller).filter_by(id=controllerID).one()
                    controller.Value = str(currentValue)
                    s.commit()
                    hard_id_high = (hard_id >> (8 * 1)) & 0xff
                    hard_id_low = (hard_id >> (8 * 0)) & 0xff
                    send2board = [hard_id_low, hard_id_high, l, h, 111, 6, 1, 2, 0, 0]
                    sendWrap = SendWrap(device_listen_addr)
                    sendWrap.SendData(send2board)
                if opt is 1:
                    # 打开一个灯
                    cl = int(cur_value) & 0xff
                    ch = int(cur_value) & 0xff00
                    ch = ch >> 8
                    # 通过point index 组装 发送数据
                    l = 0
                    h = 0
                    oneN = 1
                    if int(pointIndex) > 8:
                        h = oneN << (int(pointIndex) - 8 - 1)
                    else:
                        l = oneN << int(pointIndex - 1)
                    # 通过opt组合发送的数据
                    l = l | cl
                    h = h | ch
                    # 写会数据库下次使用
                    currentValue = (h << 8 | l) & 0xffff
                    controller = s.query(Controller).filter_by(id=controllerID).one()
                    controller.Value = str(currentValue)
                    s.commit()
                    hard_id_high = (hard_id >> (8 * 1)) & 0xff
                    hard_id_low = (hard_id >> (8 * 0)) & 0xff
                    send2board = [hard_id_low, hard_id_high, l, h, 0, 0, 0, 0, 0, 0]
                    sendWrap = SendWrap(device_listen_addr)
                    sendWrap.SendData(send2board)
        s.close()
        SetTaskInRunning(self.task, False)
        logging.info('session closed')

    def CheckError(self):
        s = Session()
        no = s.query(Normal, OperationSet).filter(Normal.id == OperationSet.NormalID).filter(
            Normal.id == int(self.task.Params)).order_by(OperationSet.id).all()
        ret = {'code': 0,
               'desc': 'success'}
        if len(no) is 0:
            ret['code'] = 1
            ret['desc'] = 'start work id error: do not find any data'
        return ret

    def stop(self):
        self.stop()
        # threading.Thread._Thread__stop(self)

    def run(self):
        self.TimeWorkerThread(self.WorkingThread_args)

    def Start(self):
        self.WorkingThread_args = self.task.Params
        super(TimeWorker, self).start()


def CanRunSunMarkTask(d, delay_time):
    delay = datetime.timedelta(seconds=delay_time)
    etime = d + delay
    now = datetime.datetime.now()
    if now.year == etime.year and now.month == etime.month and \
            now.day == etime.day and now.hour == etime.hour and \
            now.min == etime.min and now.second == etime.second:
        return True
    return False


def is_timepoint_sec(tp):
    pointlist = tp.split('-')
    year = int(pointlist[0])
    mouth = int(pointlist[1])
    day = int(pointlist[2])
    hour = int(pointlist[3])
    minites = int(pointlist[4])
    seconds = int(pointlist[5])
    times = time.localtime()
    if year == times.tm_year and \
            mouth == times.tm_mon and \
            day == times.tm_mday and \
            hour == times.tm_hour and \
            minites == times.tm_min and \
            seconds == times.tm_sec:
        return True
    return False


def IsNoneConfigCheck(checkValue):
    if type(checkValue) is str:
        if checkValue is "" or checkValue is '0' or checkValue is "''" or checkValue is "\"\"":
            return True
    if type(checkValue) is int:
        a = inspect.stack()[0][3]
        raise Exception(a + ' function can not accept int arguments ')
    return False


def is_daytime_sec(strt):
    a = type(strt)
    if not (a is str or a is unicode):
        return False
    pointlist = strt.split(u'-')
    hour = int(pointlist[0])
    minites = int(pointlist[1])
    seconds = int(pointlist[2])
    times = time.localtime()
    if hour == times.tm_hour and \
            minites == times.tm_min and \
            seconds == times.tm_sec:
        return True
    return False


def can_pass_week_day_check(strw):
    if not (type(strw) is unicode or type(strw) is not str):
        return False
    if strw is '' or strw is "''" or strw is "\"\"" or strw is "[]":
        return True
    try:
        weeks = json.loads(strw)
    except Exception as e:
        print('recv exception : ' + str(e.message))
        return False
    if not (weeks and len(weeks) > 0):
        return True
    times = time.localtime()
    if times.tm_wday in weeks:
        return True
    return False


def SendToBoardPointOpt(pointId, opt):
    session = Session()
    points = session.query(ControllerPoint).with_for_update(read=False, nowait=False).filter_by(
        id=pointId).all()
    if len(points) != 1:
        return
    point = points[0]
    curentValue = point.controller.Value
    pointIndex = point.PointIndex
    hard_id = point.controller.HardID

    cl = int(curentValue) & 0xff
    ch = int(curentValue) & 0xff00
    ch = ch >> 8

    # 通过point index 组装 发送数据
    l = 0
    h = 0
    oneN = 1

    if int(pointIndex) > 8:
        h = oneN << (int(pointIndex) - 8 - 1)
    else:
        l = oneN << int(pointIndex - 1)

    # 通过opt组合发送的数据
    if opt is 1:
        l = l | cl
        h = h | ch
    if opt is 2:
        h = ~h & ch
        l = ~l & cl
    # 写会数据库下次使用
    curentValue = (h << 8 | l) & 0xffff
    controller = point.controller
    controller.Value = str(curentValue)
    session.commit()
    # time.sleep(0.2)
    send2board = [0] * 10
    send2board[1] = (hard_id >> 8) & 0xff
    send2board[0] = (hard_id >> 0) & 0xff
    send2board[2] = l
    send2board[3] = h
    send2board[4] = 0  # board_mark >> 8 & 0xff
    send2board[5] = 0  # board_mark >> 8 * 0 & 0xff
    send2board[6] = 0  # (hard_id >> 8) & 0xff
    send2board[7] = 0  # (hard_id >> 0) & 0xff
    send2board[8] = 0
    send2board[9] = 0
    intret = 0
    if global_status.ai_ele_global_status.config['with-hardwork']:
        sendWrap = SendWrap(device_listen_addr)
        intret = sendWrap.SendData(send2board)
    return intret


def run_a_normal_task(task):
    logging.debug('start time run a normal task')
    SetTaskInRunning(task, True)
    if task.OptTypeID is 4:
        # 运行一个模式
        iid = task.Params
        # awork = WorkManager(iid)
        awork = TimeWorker(task)
        # is_ok = awork.CheckError()
        # if is_ok['code'] is not 0:
        #    return
        awork.Start()
        return
    if task.OptTypeID is 5:
        # 停止一个模式
        iid = task.Params
        tasklist = global_status.ai_ele_global_status.Get4NormalMap(str(iid))
        for t in tasklist:
            t.stop()

        return
    SendToBoardPointOpt(task.PointID, task.OptTypeID)
    SetTaskInRunning(task, False)


class TimingSwitchThread(threading.Thread):
    def __init(self):
        pass

    def run(self):

        session = sessionmaker(bind=engine, autoflush=True)

        while True:
            s = session()
            try:
                time.sleep(0.9)
                tms = s.query(TimingSwitch).with_for_update(read=False, nowait=False).filter_by(enable=1,
                                                                                                IsRunning=0).all()
                if len(tms) is 0:
                    continue
                for i in tms:
                    timepoint = str(i.wallTime)
                    u, d = sun_run_pred.get_sun_up2()
                    if not IsNoneConfigCheck(timepoint):
                        if is_timepoint_sec(timepoint):
                            run_a_normal_task(i)
                    elif not IsNoneConfigCheck(str(i.everyDay)):
                        if is_daytime_sec(i.everyDay):
                            if can_pass_week_day_check(i.weekDay):
                                logging.info('执行daytime和weekday联合限制的任务')
                                run_a_normal_task(i)
                    else:
                        pass
                    if i.sunup_lightoff is 1:
                        delay_time = int(i.sunup_lightoff_delay)
                        if CanRunSunMarkTask(u, delay_time):
                            logging.info('执行日出关灯任务', delay_time)
                            run_a_normal_task(i)
                    if i.sunup_lighton is 1:
                        delay_time = int(i.sunup_lighton_delay)
                        if CanRunSunMarkTask(d, delay_time):
                            logging.info('执行日出开灯任务', delay_time)
                            run_a_normal_task(i)
                    if i.sundown_lightoff is 1:
                        delay_time = int(i.sundown_lightoff_delay)
                        if CanRunSunMarkTask(d, delay_time):
                            logging.info('执行日落关灯任务')
                            run_a_normal_task(i)
                    if i.sundown_lighton is 1:
                        delay_time = int(i.sundown_lighton_delay)
                        if CanRunSunMarkTask(d, delay_time):
                            logging.info('执行日落开灯任务', delay_time)
                            run_a_normal_task(i)
            except Exception as e:
                logging.warn(e.message)
                # logging.warn(inspect.trace()[0][3], e)
